package com.wxscrmplus.common.core.controller;

import cn.hutool.core.util.StrUtil;
import com.wxscrmplus.common.config.WxscrmPlusConfig;
import com.wxscrmplus.common.core.domain.R;
import com.wxscrmplus.common.core.domain.model.LoginUser;
import com.wxscrmplus.common.core.service.UserService;
import com.wxscrmplus.common.exception.DemoModeException;
import com.wxscrmplus.common.exception.ServiceException;
import com.wxscrmplus.common.helper.LoginHelper;
import com.wxscrmplus.common.utils.ServletUtils;
import com.wxscrmplus.common.utils.StringUtils;
import com.wxscrmplus.common.utils.spring.SpringUtils;
import io.micrometer.core.instrument.util.IOUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ModelAttribute;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * web层通用数据处理
 *
 * @author www.wxscrmplus.com
 */
@Slf4j
public class BaseController {

    @Autowired
    private WxscrmPlusConfig wxscrmPlusConfig;

    /**
     * 响应返回结果
     *
     * @param rows 影响行数
     * @return 操作结果
     */
    protected R<Void> toAjax(int rows) {
        return rows > 0 ? R.ok() : R.fail();
    }

    /**
     * 响应返回结果
     *
     * @param result 结果
     * @return 操作结果
     */
    protected R<Void> toAjax(boolean result) {
        return result ? R.ok() : R.fail();
    }

    /**
     * 页面跳转
     */
    public String redirect(String url) {
        return StringUtils.format("redirect:{}", url);
    }

    /**
     * 获取用户缓存信息
     */
    public LoginUser getLoginUser() {
        return LoginHelper.getLoginUser();
    }

    /**
     * 获取登录用户id
     */
    public Long getUserId() {
        return LoginHelper.getUserId();
    }

    /**
     * 获取登录部门id
     */
    public Long getDeptId() {
        return LoginHelper.getDeptId();
    }

    /**
     * 获取登录用户名
     */
    public String getUsername() {
        return LoginHelper.getUsername();
    }

    /**
     * 获取登录用户部门下的成员userId
     */
    public List<Long> getDeptUserIds() {
        return SpringUtils.getBean(UserService.class).selectUserIdsByDeptId(getDeptId());
    }
// 192ea4c5544eeb76127a60a6133c6ddc

    /**
     * 获取登录用户数据权限下的所有userID
     */
    public List<Long> getAllUserIds() {
        return SpringUtils.getBean(UserService.class).selectUserIdsByDeptId(null);
    }


    private static String getBody(HttpServletRequest request) {
        try (InputStream is = request.getInputStream()) {
            return IOUtils.toString(is, StandardCharsets.UTF_8);
        } catch (IOException ex) {
            log.error("read http request failed.", ex);
        }
        return "";
    }


    @ModelAttribute
    public void init(HttpServletRequest httpServletRequest, HttpServletResponse response) throws IOException {
        // 增删改 请求
        boolean isDemoEnabled = wxscrmPlusConfig.isDemoEnabled();
        //加入了一个是否是演示模式的判断
        if (isDemoEnabled && ("DELETE".equals(httpServletRequest.getMethod()) || "POST".equals(httpServletRequest.getMethod()) || "PUT".equals(httpServletRequest.getMethod()))) {
            throw new DemoModeException();
        }
        String queryString = getBody(httpServletRequest);
        if (("POST".equals(httpServletRequest.getMethod()) || "PUT".equals(httpServletRequest.getMethod())) &&
            StrUtil.isNotBlank(queryString)) {
            queryString = queryString
                .replace("\"fieldset\"", "")
                .replace("\"label\"", "")
                .replace("\"legend\"", "")
                .replace("\"area\"", "")
                .replace("\"map\"", "")
                .replace("\"param\"", "")
                .replace("\"xml\"", "")
                .replace("\"script\"", "")
                .replace("\"iframe\"", "")
                .replace("\"link\"", "")
                .replace("\"style\"", "")
                .replace("\"meta\"", "")
                .replace("\"base\"", "")
                .replace("\"object\"", "")
                .replace("\"applet\"", "")
                .replace("\"embed\"", "")
                .replace("\"form\"", "")
                .replace("\"input\"", "")
                .replace("\"textarea\"", "")
                .replace("\"button\"", "")
                .replace(",", "")
                .replace("]", "")
                .replace("[", "")
                .replace("}", "")
                .replace("{", "")
                .replace(":", "")
                .replace("\"", "");
//            checkSecurity(queryString);
        }

    }


    /**
     * 判断输入文本是否存在攻击脚本字符
     *
     * @param input 待校验的文本
     */
    public void checkSecurity(String input) {
        if (input == null || input.isEmpty()) {
            return;
        }

        // 判断是否包含脚本标签
        String regexScript = "<[\\s]*script[^>]*>[\\s\\S]*<[\\s]*/[\\s]*script[\\s]*>";
        Pattern patternScript = Pattern.compile(regexScript, Pattern.CASE_INSENSITIVE);
        if (patternScript.matcher(input).find()) {
            log.warn("用户正在尝试输入 脚本标签 请求IP:{}，参数：{}", ServletUtils.getClientIP(), input);
            throw new ServiceException("不可输入敏感字符");
        }

        // 判断是否包含 CSS 样式
        String regexCSS = "<[\\s]*style[^>]*>[\\s\\S]*<[\\s]*/[\\s]*style[\\s]*>";
        Pattern patternCSS = Pattern.compile(regexCSS, Pattern.CASE_INSENSITIVE);
        if (patternCSS.matcher(input).find()) {
            log.warn("用户正在尝试输入 CSS 样式 请求IP:{}，参数：{}", ServletUtils.getClientIP(), input);
            throw new ServiceException("不可输入敏感字符");
        }

        // 判断是否包含危险的 HTML 标签
        String regexHtml = "<(\\s*\\S+\\s*)([\\s\\S]*)(\\/?>)";
        Pattern patternHtml = Pattern.compile(regexHtml, Pattern.CASE_INSENSITIVE);
        Matcher matcherHtml = patternHtml.matcher(input);
        while (matcherHtml.find()) {
            String tagName = matcherHtml.group(1).toLowerCase();
            if (tagName.matches("script|iframe|link|style|meta|base|object|applet|embed|form|input|textarea|button|select|option|optgroup|label|fieldset|legend|area|map|param|xml")) {
                log.warn("用户正在尝试输入 危险的 HTML 标签 请求IP:{}，参数：{}", ServletUtils.getClientIP(), input);
                throw new ServiceException("不可输入敏感字符");
            }
        }

        // 判断是否包含 JavaScript 代码
        String regexJs = "javascript:(.*)";
        Pattern patternJs = Pattern.compile(regexJs, Pattern.CASE_INSENSITIVE);
        if (patternJs.matcher(input).find()) {
            log.warn("用户正在尝试输入 JavaScript 代码 请求IP:{}，参数：{}", ServletUtils.getClientIP(), input);
            throw new ServiceException("不可输入敏感字符");
        }

        // 判断是否包含 SQL 注入关键字
        String regexSql = "(?i)(select |update |delete |insert |truncate |drop |alter |create |grant |revoke |execute )";
        Pattern patternSql = Pattern.compile(regexSql, Pattern.CASE_INSENSITIVE);
        if (patternSql.matcher(input).find()) {
            log.warn("用户正在尝试输入 SQL 注入关键字 请求IP:{}，参数：{}", ServletUtils.getClientIP(), input);
            throw new ServiceException("不可输入敏感字符");
        }

        // 判断是否包含 OS 命令注入关键字
        String regexOs = "((?i)\\b(cat |chmod |chown |curl |echo |killall |kill |ln |ls |mkdir |mkfs |mount |mv |nc |ncat |netstat |ping |ps |pwd |rm |rmdir |sed |ssh |sudo |tee |telnet |vi |vim |wget )\\b)";
        Pattern patternOs = Pattern.compile(regexOs, Pattern.CASE_INSENSITIVE);
        if (patternOs.matcher(input).find()) {
            log.warn("用户正在尝试输入 OS 命令注入关键字 请求IP:{}，参数：{}", ServletUtils.getClientIP(), input);
            throw new ServiceException("不可输入敏感字符");
        }

        // 判断是否包含 XXE 注入关键字
        String regexXXE = "(?i)(<!ENTITY|<!DOCTYPE|<\\?xml|SYSTEM|PUBLIC)";
        Pattern patternXXE = Pattern.compile(regexXXE, Pattern.CASE_INSENSITIVE);
        if (patternXXE.matcher(input).find()) {
            log.warn("用户正在尝试输入 XXE 注入关键字 请求IP:{}，参数：{}", ServletUtils.getClientIP(), input);
            throw new ServiceException("不可输入敏感字符");
        }
        // 判断是否包含 XPath 注入关键字
        String regexXPath = "(?i)(extractvalue |updatexml |name |count |concat |version |substring |substring-after |substring-before |starts-with |contains |ends-with |local-name |namespace-uri )";
        Pattern patternXPath = Pattern.compile(regexXPath, Pattern.CASE_INSENSITIVE);
        if (patternXPath.matcher(input).find()) {
            log.warn("用户正在尝试输入 XPath 注入关键字 请求IP:{}，参数：{}", ServletUtils.getClientIP(), input);
            throw new ServiceException("不可输入敏感字符");
        }

        // 判断是否包含 LDAP 注入关键字
        String regexLDAP = "(?i)([\\\\|*|(|)|{|}|\\[|\\]|&])|(\\b(and|or)\\b)";
        Pattern patternLDAP = Pattern.compile(regexLDAP, Pattern.CASE_INSENSITIVE);
        if (patternLDAP.matcher(input).find()) {
            log.warn("用户正在尝试输入 LDAP 注入关键字 请求IP:{}，参数：{}", ServletUtils.getClientIP(), input);
            throw new ServiceException("不可输入敏感字符");
        }
        // 判断是否包含 URL 编码相关攻击
        String regexUrl = "(%[0-9a-fA-F]{2}){2,}";
        Pattern patternUrl = Pattern.compile(regexUrl);
        if (patternUrl.matcher(input).find()) {
            log.warn("用户正在尝试输入 URL 编码相关攻击 请求IP:{}，参数：{}", ServletUtils.getClientIP(), input);
            throw new ServiceException("不可输入敏感字符");
        }
    }
}
